package com.example.common.utils;

import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;

import com.coszero.utilslibrary.utils.LogX;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Desc： 线程池工具类
 *
 * @author xmqian
 * Email:xmqian93@163.com
 * Date: 2020/8/14 0014 10:20
 * @link(https://blog.csdn.net/weixin_43115440/article/details/90479752)
 * @version 1
 */
public class ExecutorUtils {
    private static final String TAG = "### ExecutorUtils";
    /**
     * 磁盘IO线程池
     **/
    private final ExecutorService diskIO;
    /**
     * 网络IO线程池
     **/
    private final ExecutorService networkIO;
    /**
     * UI线程
     **/
    private final Executor mainThread;
    /**
     * 定时任务线程池
     **/
    private final ScheduledExecutorService scheduledExecutor;

    private volatile static ExecutorUtils ExecutorUtils;

    public static ExecutorUtils getInstance() {
//        if (ExecutorUtils == null) {
//            synchronized (ExecutorUtils.class) {
        if (ExecutorUtils == null) {
            ExecutorUtils = new ExecutorUtils();
        }
//            }
//        }
        return ExecutorUtils;
    }

    private ExecutorUtils(ExecutorService diskIO, ExecutorService networkIO, Executor mainThread, ScheduledExecutorService scheduledExecutor) {
        this.diskIO = diskIO;
        this.networkIO = networkIO;
        this.mainThread = mainThread;
        this.scheduledExecutor = scheduledExecutor;
    }

    private ExecutorUtils() {
        this(diskIoExecutor(), networkExecutor(), new MainThreadExecutor(), scheduledThreadPoolExecutor());
    }

    /**
     * schedule()单延迟任务
     * scheduleAtFixedRate(）是按照上一次任务的发起时间计算下一次任务的开始时间,会被内部任务影响
     * scheduleWithFixedDelay() 以上一次任务的结束时间计算下一次任务的开始时间
     * #3个方法都会有一个如下返回值
     * ScheduledFuture<?> scheduledFuture;
     * scheduledFuture.cancel(false); 取消定时器(等待当前任务结束后，取消定时器)
     * scheduledFuture.cancel(true); 取消定时器(不等待当前任务结束，取消定时器)
     * 定时(延时)任务线程池
     * <p>
     * 替代Timer,执行定时任务,延时任务
     */
    public ScheduledExecutorService scheduledExecutor() {
        int activeCount = ((ThreadPoolExecutor) scheduledExecutor).getActiveCount();
        LogX.i(TAG, "[scheduledExecutor]:current alive thread count =" + activeCount);
        return scheduledExecutor;
    }

    /**
     * 磁盘IO线程池（单线程）
     * <p>
     * 和磁盘操作有关的进行使用此线程(如读写数据库,读写文件)
     * 禁止延迟,避免等待
     * 此线程不用考虑同步问题
     */
    public ExecutorService diskIO() {
        return diskIO;
    }

    /**
     * 网络IO线程池
     * <p>
     * 网络请求,异步任务等适用此线程
     * 不建议在这个线程 sleep 或者 wait
     */
    public ExecutorService networkIO() {
        int activeCount = ((ThreadPoolExecutor) networkIO).getActiveCount();
        LogX.i(TAG, "[networkIO]:current alive thread count =" + activeCount);
        return networkIO;
    }

    /**
     * UI线程
     * <p>
     * Android 的MainThread
     * UI线程不能做的事情这个都不能做
     */
    public Executor mainThread() {
        return mainThread;
    }

    private static ScheduledExecutorService scheduledThreadPoolExecutor() {
        return new ScheduledThreadPoolExecutor(5, new MyThreadFactory("scheduled_executor"), new ThreadPoolExecutor.AbortPolicy());
    }

    private static ExecutorService diskIoExecutor() {
        return Executors.newSingleThreadExecutor(new MyThreadFactory("single"));
    }

    private static ExecutorService networkExecutor() {
        return Executors.newFixedThreadPool(3, new MyThreadFactory("fixed"));
    }


    private static class MainThreadExecutor implements Executor {
        private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
            mainThreadHandler.post(command);
        }
    }

    private static class MyThreadFactory implements ThreadFactory {
        private final String name;
        private int count = 0;

        public MyThreadFactory(String name) {
            this.name = name;
        }

        @Override
        public Thread newThread(Runnable r) {
            count++;
            return new Thread(r, name + "-" + count + "-Thread");
        }
    }

}
